#include "../Buffer/Buffer.h"
#include "Scanner.h"
#include "Automat.h"
#include "../Symboltable/IInformation.h"
#include "../Symboltable/Information.h"
#include "InformationInteger.h"
#include "Token.h"

#include <string.h>
#include <stddef.h>
#include <limits.h>

Scanner::Scanner(Buffer& newBuffer) :
	buffer(&newBuffer), lexemStorage(1024), symbolTable(1024), tokenLenght(0) {
	automat = new Automat(this);

	int intlen = strlen("int");
	int readlen = strlen("read");
	char* intstr = lexemStorage.malloc(intlen + 2);
	char* readstr = lexemStorage.malloc(readlen + 2);

	intstr[0] = intlen;
	readstr[0] = readlen;
	strcpy(&intstr[1], "int");
	strcpy(&readstr[1], "read");
	symbolTable.insert(intstr, INT_TYPE);
	symbolTable.insert(readstr, READ_TYPE);
}

Scanner::~Scanner() {
	delete automat;
	// buffer will be deleted by caller
}

bool Scanner::nextToken(Token& token) {
	TokenType type;

	char c;
	do {
		c = buffer->getChar();
		type = automat->handle(c);

		// TODO: write line counter class
		positionCounter.handle(c);
		if (type == NONE) {
			++tokenLenght;
		}
	} while (type == NONE || type == SKIP);

	mkToken(type, token);
	return (type != END_OF_FILE);
}

void Scanner::freeToken(Token* token) {
	delete token;
}

void Scanner::mkToken(TokenType tokenType, Token& token) {
	static SharedPointer<IInformation> info;
	static char* lexem;

	if (tokenLenght) {
		// assuming char[0] = length of string
		lexem = lexemStorage.malloc(tokenLenght + 2);
		buffer->copyString(tokenLenght, lexem);
	}

	do {
		switch (tokenType) {
		case END_OF_FILE: {
			// TODO: implement
			// info = SharedPointer<InformationEndOfFile> (new InformationEndOfFile());
			break;
		}
		case INTEGER: {
			int number = 0;

			for (size_t i = 1; i <= tokenLenght; ++i) {
				if (number * 10 + 9 > INT_MAX) {
					tokenType = ERROR;
					break;
				}
				number *= 10;
				number += (lexem[i] - '0');
			}

			// number too long
			if (tokenType == ERROR) {
				continue;
			}

			info = SharedPointer<IInformation> (
					new InformationInteger(lexem,number));
			break;
		}
		case COMMENT: {
			// TODO: implement
			// info = SharedPointer<InformationComment> (new InformationComment(lexem));
			break;
		}
		case ERROR: {
			// TODO: implement
			// info = SharedPointer<InformationError> (new InformationError(lexem));
			break;
		}
		case IDENTIFIER:
		case RESERVED_WORD: {
			info = symbolTable.lookup(symbolTable.insert(lexem, tokenType));
			tokenType = info->getType();
			break;
		}
		default:
			break;
		}
	} while (false);

	token.setLine(positionCounter.getRow());
	token.setColumn(positionCounter.getColumn());
	token.setInformation(info);
	token.setType(tokenType);
}

int Scanner::ungetChar(const size_t i) {
	size_t count = buffer->ungetChar(i);
	positionCounter.goBack(count);
	if (count != i) {
		puts("count != i\n");
	}
	return count;
}

void Scanner::stop() {
	// not used
}
